﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Xml;

namespace XmlDSigEx
{
    public class ECDsaAlgorithm:ISignatureAlgorithm
    {
        private ECDsaCng _algorithm;

        internal const string URI_ECDSA_PREFIX = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-";

        public ECDsaAlgorithm(CngKey key)
        {
            _algorithm = new ECDsaCng(key);
        }


        internal ECDsaAlgorithm(XmlElement signatureElement)
        {
            XmlNamespaceManager manager = new XmlNamespaceManager(signatureElement.OwnerDocument.NameTable);
            manager.AddNamespace("ds", XmlSigner.NS_XMLDSIG);
            XmlElement keyInfoElement = signatureElement.SelectSingleNode("//ds:KeyInfo",manager) as XmlElement;
            foreach (XmlElement keyInfoChild in keyInfoElement.ChildNodes)
            {
                if (keyInfoChild.LocalName == "ECDSAKeyValue")
                {
                    ProcessECKeyData(keyInfoChild);
                    return;
                }
            }
        }

        private void ProcessECKeyData(XmlElement keyInfoChild)
        {
            _algorithm = new ECDsaCng();
            _algorithm.FromXmlString(keyInfoChild.OuterXml, ECKeyXmlFormat.Rfc4050);
        }


        public byte[] SignData(System.IO.Stream data, string signatureAlgorithm)
        {
            _algorithm.HashAlgorithm = GetHashAlgorithm(signatureAlgorithm);
            return _algorithm.SignData(data);
        }

        private CngAlgorithm GetHashAlgorithm(string signatureAlgorithm)
        {
            switch (signatureAlgorithm.Remove(0, URI_ECDSA_PREFIX.Length))
            {
                case "sha1":
                    return CngAlgorithm.Sha1;

                case "sha256":
                    return CngAlgorithm.Sha256;

                case "sha384":
                    return CngAlgorithm.Sha384;

                case "sha512":
                    return CngAlgorithm.Sha512;

                default:
                    throw new UnknownAlgorithmException("The signature algorithm was not understood.");
            }
        }

        public bool VerifySignature(System.IO.Stream data, byte[] signature, string signatureAlgorithm)
        {
            _algorithm.HashAlgorithm = GetHashAlgorithm(signatureAlgorithm);
            return _algorithm.VerifyData(data, signature);
        }

        public void WriteKeyInfo(System.Xml.XmlWriter writer)
        {
            writer.WriteRaw(_algorithm.ToXmlString(ECKeyXmlFormat.Rfc4050));
        }

        public bool WillWriteKeyInfo
        {
            get { return true; }
        }
    }
}
